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 d4b066b95..490244fda 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 @@ -17,7 +17,17 @@ import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.QueryParser; -import org.apache.lucene.search.*; +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; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +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; @@ -37,18 +47,18 @@ import com.mysema.query.types.PathType; * */ public class LuceneSerializer { - - private static final Map,Integer> sortFields = new HashMap,Integer>(); - - static{ - sortFields.put(Integer.class, SortField.INT); - sortFields.put(Float.class, SortField.FLOAT); - sortFields.put(Long.class, SortField.LONG); - sortFields.put(Double.class, SortField.DOUBLE); - sortFields.put(Short.class, SortField.SHORT); - sortFields.put(Byte.class, SortField.BYTE); - sortFields.put(BigDecimal.class, SortField.DOUBLE); - sortFields.put(BigInteger.class, SortField.LONG); + + private static final Map, Integer> sortFields = new HashMap, Integer>(); + + static { + sortFields.put(Integer.class, SortField.INT); + sortFields.put(Float.class, SortField.FLOAT); + sortFields.put(Long.class, SortField.LONG); + sortFields.put(Double.class, SortField.DOUBLE); + sortFields.put(Short.class, SortField.SHORT); + sortFields.put(Byte.class, SortField.BYTE); + sortFields.put(BigDecimal.class, SortField.DOUBLE); + sortFields.put(BigInteger.class, SortField.LONG); } public static final LuceneSerializer DEFAULT = new LuceneSerializer(false, true); @@ -96,22 +106,36 @@ public class LuceneSerializer { return le(operation); } else if (op == Ops.GOE || op == Ops.AOE) { return ge(operation); - } else if (op == PathType.DELEGATE){ + } else if (op == PathType.DELEGATE) { return toQuery(operation.getArg(0)); } throw new UnsupportedOperationException("Illegal operation " + operation); } private Query toTwoHandSidedQuery(Operation operation, Occur occur) { - // TODO Flatten similar queries(?) Query lhs = toQuery(operation.getArg(0)); Query rhs = toQuery(operation.getArg(1)); BooleanQuery bq = new BooleanQuery(); - bq.add(lhs, occur); - bq.add(rhs, occur); + bq.add(createBooleanClause(lhs, occur)); + bq.add(createBooleanClause(rhs, occur)); return bq; } + /** + * If the query is a BooleanQuery and it contains a single Occur.MUST_NOT + * clause it will be returned as is. Otherwise it will be wrapped in a + * BooleanClause with the given Occur. + */ + private BooleanClause createBooleanClause(Query query, Occur occur) { + if (query instanceof BooleanQuery) { + BooleanClause[] clauses = ((BooleanQuery) query).getClauses(); + if (clauses.length == 1 && clauses[0].getOccur().equals(Occur.MUST_NOT)) { + return clauses[0]; + } + } + return new BooleanClause(query, occur); + } + private Query like(Operation operation) { verifyArguments(operation); String field = toField(operation.getArg(0)); @@ -125,38 +149,39 @@ public class LuceneSerializer { } return new WildcardQuery(new Term(field, normalize(terms[0]))); } - + @SuppressWarnings("unchecked") private Query eq(Operation operation) { verifyArguments(operation); String field = toField(operation.getArg(0)); - if (Number.class.isAssignableFrom(operation.getArg(1).getType())){ - return new TermQuery(new Term(field, convertNumber(((Constant)operation.getArg(1)).getConstant()))); - }else{ - return eq(field, createTerms(operation.getArg(1))); + if (Number.class.isAssignableFrom(operation.getArg(1).getType())) { + return new TermQuery(new Term(field, convertNumber(((Constant) operation + .getArg(1)).getConstant()))); } + return eq(field, createTerms(operation.getArg(1))); } - - private String convertNumber(Number number){ - if (Integer.class.isInstance(number)){ - return NumericUtils.intToPrefixCoded(number.intValue()); - }else if (Double.class.isInstance(number)){ - return NumericUtils.doubleToPrefixCoded(number.doubleValue()); - }else if (Long.class.isInstance(number)){ - return NumericUtils.longToPrefixCoded(number.longValue()); - }else if (Float.class.isInstance(number)){ - return NumericUtils.floatToPrefixCoded(number.floatValue()); - }else if (Byte.class.isInstance(number)){ - return NumericUtils.intToPrefixCoded(number.intValue()); - }else if (Short.class.isInstance(number)){ - return NumericUtils.intToPrefixCoded(number.intValue()); - }else if (BigDecimal.class.isInstance(number)){ - return NumericUtils.doubleToPrefixCoded(number.doubleValue()); - }else if (BigInteger.class.isInstance(number)){ - return NumericUtils.longToPrefixCoded(number.longValue()); - }else{ - throw new IllegalArgumentException("Unsupported numeric type " + number.getClass().getName()); - } + + private String convertNumber(Number number) { + if (Integer.class.isInstance(number)) { + return NumericUtils.intToPrefixCoded(number.intValue()); + } else if (Double.class.isInstance(number)) { + return NumericUtils.doubleToPrefixCoded(number.doubleValue()); + } else if (Long.class.isInstance(number)) { + return NumericUtils.longToPrefixCoded(number.longValue()); + } else if (Float.class.isInstance(number)) { + return NumericUtils.floatToPrefixCoded(number.floatValue()); + } else if (Byte.class.isInstance(number)) { + return NumericUtils.intToPrefixCoded(number.intValue()); + } else if (Short.class.isInstance(number)) { + return NumericUtils.intToPrefixCoded(number.intValue()); + } else if (BigDecimal.class.isInstance(number)) { + return NumericUtils.doubleToPrefixCoded(number.doubleValue()); + } else if (BigInteger.class.isInstance(number)) { + return NumericUtils.longToPrefixCoded(number.longValue()); + } else { + throw new IllegalArgumentException("Unsupported numeric type " + + number.getClass().getName()); + } } private Query eq(String field, String[] terms) { @@ -234,7 +259,8 @@ public class LuceneSerializer { private Query between(Operation operation) { verifyArguments(operation); // TODO Phrase not properly supported - return range(toField(operation.getArg(0)), operation.getArg(1), operation.getArg(2), true, true); + return range(toField(operation.getArg(0)), operation.getArg(1), operation.getArg(2), true, + true); } private Query lt(Operation operation) { @@ -259,33 +285,34 @@ public class LuceneSerializer { @SuppressWarnings("unchecked") private Query range(String field, Expr min, Expr max, boolean minInc, boolean maxInc) { - if (min != null && Number.class.isAssignableFrom(min.getType()) || max != null && Number.class.isAssignableFrom(max.getType())) { - Class numType = (Class)(min != null ? min.getType() : max.getType()); - return numericRange((Class)numType, field, - (Number)(min == null ? null : ((Constant) min).getConstant()), - (Number)(max == null ? null : ((Constant) max).getConstant()), - minInc, maxInc); - } else { - return stringRange(field, min, max, minInc, maxInc); + if (min != null && Number.class.isAssignableFrom(min.getType()) || max != null + && Number.class.isAssignableFrom(max.getType())) { + Class numType = (Class) (min != null ? min.getType() : max.getType()); + return numericRange((Class) numType, field, (Number) (min == null ? null + : ((Constant) min).getConstant()), (Number) (max == null ? null + : ((Constant) max).getConstant()), minInc, maxInc); } + return stringRange(field, min, max, minInc, maxInc); } - - private NumericRangeQuery numericRange(Class clazz, String field, N min, N max, boolean minInc, boolean maxInc){ - if (clazz.equals(Integer.class)){ - return NumericRangeQuery.newIntRange(field, (Integer)min, (Integer)max, minInc, maxInc); - }else if (clazz.equals(Double.class)){ - return NumericRangeQuery.newDoubleRange(field, (Double)min, (Double)max, minInc, minInc); - }else if (clazz.equals(Float.class)){ - return NumericRangeQuery.newFloatRange(field, (Float)min, (Float)max, minInc, minInc); - }else if (clazz.equals(Long.class)){ - return NumericRangeQuery.newLongRange(field, (Long)min, (Long)max, minInc, minInc); - }else if (clazz.equals(Byte.class) || clazz.equals(Short.class)){ - return NumericRangeQuery.newIntRange(field, - min != null ? min.intValue() : null, - max != null ? max.intValue() : null, minInc, maxInc); - }else{ - throw new IllegalArgumentException("Unsupported numeric type " + clazz.getName()); - } + + private NumericRangeQuery numericRange(Class clazz, String field, + N min, N max, boolean minInc, boolean maxInc) { + if (clazz.equals(Integer.class)) { + return NumericRangeQuery.newIntRange(field, (Integer) min, (Integer) max, minInc, + maxInc); + } else if (clazz.equals(Double.class)) { + return NumericRangeQuery.newDoubleRange(field, (Double) min, (Double) max, minInc, + minInc); + } else if (clazz.equals(Float.class)) { + return NumericRangeQuery.newFloatRange(field, (Float) min, (Float) max, minInc, minInc); + } else if (clazz.equals(Long.class)) { + return NumericRangeQuery.newLongRange(field, (Long) min, (Long) max, minInc, minInc); + } else if (clazz.equals(Byte.class) || clazz.equals(Short.class)) { + return NumericRangeQuery.newIntRange(field, min != null ? min.intValue() : null, + max != null ? max.intValue() : null, minInc, maxInc); + } else { + throw new IllegalArgumentException("Unsupported numeric type " + clazz.getName()); + } } private Query stringRange(String field, Expr min, Expr max, boolean minInc, boolean maxInc) { @@ -293,9 +320,10 @@ public class LuceneSerializer { 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); - }else{ - return new TermRangeQuery(field, normalize(createTerms(min)[0]), normalize(createTerms(max)[0]), minInc, maxInc); - } + } else { + return new TermRangeQuery(field, normalize(createTerms(min)[0]), + normalize(createTerms(max)[0]), minInc, maxInc); + } } @SuppressWarnings("unchecked") @@ -319,31 +347,32 @@ public class LuceneSerializer { List> arguments = operation.getArgs(); for (int i = 1; i < arguments.size(); ++i) { if (!(arguments.get(i) instanceof Constant) - && !(arguments.get(i) instanceof PhraseElement) - && !(arguments.get(i) instanceof TermElement)) { - throw new IllegalArgumentException("operand was of unsupported type " + arguments.get(i).getClass().getName()); + && !(arguments.get(i) instanceof PhraseElement) + && !(arguments.get(i) instanceof TermElement)) { + throw new IllegalArgumentException("operand was of unsupported type " + + arguments.get(i).getClass().getName()); } } } private String[] createTerms(Expr expr) { - return split(expr, expr.toString()); + return split(expr, expr.toString()); } private String[] createEscapedTerms(Expr expr) { return split(expr, QueryParser.escape(expr.toString())); } - - private String[] split(Expr expr, String str){ - if (expr instanceof PhraseElement){ - return StringUtils.split(str); - }else if (expr instanceof TermElement){ - return new String[] {str}; - }else if (splitTerms){ - return StringUtils.split(str); - }else{ - return new String[] {str}; - } + + private String[] split(Expr expr, String str) { + if (expr instanceof PhraseElement) { + return StringUtils.split(str); + } else if (expr instanceof TermElement) { + return new String[] { str }; + } else if (splitTerms) { + return StringUtils.split(str); + } else { + return new String[] { str }; + } } private String normalize(String s) { @@ -356,7 +385,8 @@ public class LuceneSerializer { } else if (expr instanceof QueryElement) { return ((QueryElement) expr).getQuery(); } - throw new IllegalArgumentException("expr was of unsupported type " + expr.getClass().getName()); + throw new IllegalArgumentException("expr was of unsupported type " + + expr.getClass().getName()); } public Sort toSort(List> orderBys) { @@ -368,9 +398,9 @@ public class LuceneSerializer { Class type = order.getTarget().getType(); boolean reverse = !order.isAscending(); if (Number.class.isAssignableFrom(type)) { - sorts.add(new SortField(toField(order.getTarget()),sortFields.get(type),reverse)); + sorts.add(new SortField(toField(order.getTarget()), sortFields.get(type), reverse)); } else { - sorts.add(new SortField(toField(order.getTarget()),Locale.ENGLISH, reverse)); + sorts.add(new SortField(toField(order.getTarget()), Locale.ENGLISH, reverse)); } } Sort sort = new Sort(); 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 b1e1f7af4..1fa9a04b9 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 @@ -56,7 +56,7 @@ public class LuceneSerializerTest { private PString rating; private PNumber year; private PNumber gross; - + private PNumber longField; private PNumber shortField; private PNumber byteField; @@ -82,7 +82,7 @@ public class LuceneSerializerTest { 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)); - + doc.add(new NumericField("longField", Store.YES, true).setLongValue(1)); doc.add(new NumericField("shortField", Store.YES, true).setIntValue(1)); doc.add(new NumericField("byteField", Store.YES, true).setIntValue(1)); @@ -102,7 +102,7 @@ public class LuceneSerializerTest { year = entityPath.getNumber("year", Integer.class); rating = entityPath.getString("rating"); gross = entityPath.getNumber("gross", Double.class); - + longField = entityPath.getNumber("longField", Long.class); shortField = entityPath.getNumber("shortField", Short.class); byteField = entityPath.getNumber("byteField", Byte.class); @@ -193,7 +193,7 @@ public class LuceneSerializerTest { public void eq_Numeric_Double() throws Exception { testQuery(gross.eq(900.00), "gross:" + GROSS_PREFIX_CODED, 1); } - + @Test public void eq_Numeric() throws Exception{ testQuery(longField.eq(1l), "longField:" + LONG_PREFIX_CODED, 1); @@ -254,7 +254,7 @@ public class LuceneSerializerTest { @Test public void eq_not_or_eq() throws Exception { - testQuery(title.eq("House").not().or(rating.eq("Good")), "(-title:house) rating:good", 1); + testQuery(title.eq("House").not().or(rating.eq("Good")), "-title:house rating:good", 1); } @Test @@ -264,7 +264,7 @@ public class LuceneSerializerTest { @Test public void eq_and_eq_not_Does_Not_Find_Results_Because_Second_Expression_Finds_Nothing() throws Exception { - testQuery(rating.eq("Superb").and(title.eq("House").not()), "+rating:superb +(-title:house)", 0); + testQuery(rating.eq("Superb").and(title.eq("House").not()), "+rating:superb -title:house", 0); } @Test @@ -279,7 +279,7 @@ public class LuceneSerializerTest { @Test public void ne_or_eq() throws Exception { - testQuery(title.ne("Jurassic Park").or(rating.eq("Lousy")), "(-title:\"jurassic park\") rating:lousy", 0); + testQuery(title.ne("Jurassic Park").or(rating.eq("Lousy")), "-title:\"jurassic park\" rating:lousy", 0); } @Test @@ -350,7 +350,7 @@ public class LuceneSerializerTest { testQuery(floatField.between((float)0.0,(float)2.0), "floatField:[0.0 TO 2.0]", 1); } - + @Test public void between_Phrase() throws Exception { testQuery(title.between("Jurassic Park", "Kundun"), "title:[jurassic TO kundun]", 1); @@ -534,12 +534,12 @@ public class LuceneSerializerTest { public void goe_Numeric_Double_Not_Found() throws Exception { testQuery(gross.goe(900.10), "gross:[900.1 TO *]", 0); } - + @Test public void booleanBuilder() throws Exception{ testQuery(new BooleanBuilder(gross.goe(900.10)), "gross:[900.1 TO *]", 0); } - + @Test @Ignore