From 0fdb8d63fe9d46f8abc7af7692891d132ef1bf66 Mon Sep 17 00:00:00 2001 From: Vesa Martilla Date: Mon, 17 May 2010 13:58:33 +0000 Subject: [PATCH] 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. --- .../mysema/query/lucene/LuceneSerializer.java | 160 +++++++++---- .../mysema/query/lucene/LuceneQueryTest.java | 116 +++++---- .../query/lucene/LuceneSerializerTest.java | 220 ++++++++++++++++-- 3 files changed, 385 insertions(+), 111 deletions(-) diff --git a/querydsl-lucene/src/main/java/com/mysema/query/lucene/LuceneSerializer.java b/querydsl-lucene/src/main/java/com/mysema/query/lucene/LuceneSerializer.java index 5a2ac0cd5..234e65ddb 100644 --- a/querydsl-lucene/src/main/java/com/mysema/query/lucene/LuceneSerializer.java +++ b/querydsl-lucene/src/main/java/com/mysema/query/lucene/LuceneSerializer.java @@ -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) operation.getArg(1)).getConstant()))); + } else if (operation.getArg(1).getType().equals(Double.class)) { + return new TermQuery(new Term(field, NumericUtils + .doubleToPrefixCoded(((Constant) 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) min).getConstant(), max == null ? null : ((Constant) 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) min).getConstant(), max == null ? null : ((Constant) 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> 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> orderBys){ + // TODO Add support for sorting floats, longs etc. + public Sort toSort(List> orderBys) { List sortFields = new ArrayList(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; } - } diff --git a/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneQueryTest.java b/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneQueryTest.java index d2b472bae..2c2465bb1 100644 --- a/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneQueryTest.java +++ b/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneQueryTest.java @@ -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{ + public class QDocument extends PEntity { 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 year = createNumber("year", Integer.class); public final PString title = createString("title"); - + public final PNumber gross = createNumber("gross", Double.class); } private LuceneQuery query; -// private PathBuilder entityPath; private PString title; - private PString year; + private PNumber year; + private PNumber 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 results = query.listDistinctResults(); @@ -333,13 +350,14 @@ public class LuceneQueryTest { @Test public void list_All() { - List results = query.where(title.like("*")).orderBy(title.asc(), year.desc()).list(); + List 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 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 documents = query.list(); assertFalse(documents.isEmpty()); diff --git a/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneSerializerTest.java b/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneSerializerTest.java index fd7a0c895..2369166a2 100644 --- a/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneSerializerTest.java +++ b/querydsl-lucene/src/test/java/com/mysema/query/lucene/LuceneSerializerTest.java @@ -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 year; + private PNumber 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 {