package lux.query; import java.util.ArrayList; import lux.index.IndexConfiguration; import lux.query.parser.LuxQueryParser; import lux.xml.QName; import lux.xpath.LiteralExpression; import lux.xquery.AttributeConstructor; import lux.xquery.ElementConstructor; /** * Parseable analogue of TermRangeQuery and NumericRangeQuery. */ public class RangePQuery extends ParseableQuery { public enum Type { STRING(false), INT, LONG, FLOAT, DOUBLE; public boolean isNumeric; Type (boolean numeric) { isNumeric = numeric; } Type () { isNumeric = true; } }; public static final LiteralExpression FIELD_ATTR_NAME = new LiteralExpression("fieldName"); private static final LiteralExpression TYPE_ATTR_NAME = new LiteralExpression("type"); private static final LiteralExpression LOWER_TERM_ATTR_NAME = new LiteralExpression("lowerTerm"); private static final LiteralExpression UPPER_TERM_ATTR_NAME = new LiteralExpression("upperTerm"); private static final LiteralExpression INCLUDE_LOWER_ATTR_NAME = new LiteralExpression("includeLower"); private static final LiteralExpression INCLUDE_UPPER_ATTR_NAME = new LiteralExpression("includeUpper"); public static final QName TERM_RANGE_QUERY_QNAME = new QName("TermRangeQuery"); public static final QName NUMERIC_RANGE_QUERY_QNAME = new QName("NumericRangeQuery"); private String fieldName; private String lowerTerm; private String upperTerm; private boolean includeLower; private boolean includeUpper; private Type type; public RangePQuery(String fieldName, Type type, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) { this.fieldName = fieldName; this.lowerTerm = lowerTerm; this.upperTerm = upperTerm; this.includeLower = includeLower; this.includeUpper = includeUpper; // type must be one of: string, int, long, float, double. We leave the checking up to Lucene's parser, though, so in theory this coiuld be extended. this.type = type; } public String getFieldName() { return fieldName; } public String getLowerTerm() { return lowerTerm; } public String getUpperTerm() { return upperTerm; } public boolean getIncludeLower() { return includeLower; } public boolean getincludeUpper() { return includeUpper; } public Type getType() { return type; } @Override public ElementConstructor toXmlNode(String field, IndexConfiguration config) { ArrayList<AttributeConstructor> atts = new ArrayList<AttributeConstructor>(); atts.add (new AttributeConstructor(FIELD_ATTR_NAME, new LiteralExpression (fieldName))); if (lowerTerm != null) { atts.add (new AttributeConstructor(LOWER_TERM_ATTR_NAME, new LiteralExpression (lowerTerm))); } if (upperTerm != null) { atts.add (new AttributeConstructor(UPPER_TERM_ATTR_NAME, new LiteralExpression (upperTerm))); } atts.add (new AttributeConstructor(INCLUDE_LOWER_ATTR_NAME, new LiteralExpression (Boolean.toString(includeLower)))); atts.add (new AttributeConstructor(INCLUDE_UPPER_ATTR_NAME, new LiteralExpression (Boolean.toString(includeUpper)))); if (! type.isNumeric) { return new ElementConstructor (TERM_RANGE_QUERY_QNAME, LiteralExpression.EMPTY, atts.toArray(new AttributeConstructor[atts.size()])); } // TODO: precisionStep atts.add (new AttributeConstructor(TYPE_ATTR_NAME, new LiteralExpression (type.toString()))); return new ElementConstructor (NUMERIC_RANGE_QUERY_QNAME, LiteralExpression.EMPTY, atts.toArray(new AttributeConstructor[atts.size()])); } @Override public String toQueryString (String field, IndexConfiguration config) { StringBuilder buf = new StringBuilder (); buf.append(fieldName).append (':') .append (includeLower ? '[' : '{') .append (lowerTerm == null ? '*' : LuxQueryParser.escapeQParser(lowerTerm)) .append (" TO ") .append (upperTerm == null ? '*' : LuxQueryParser.escapeQParser(upperTerm)) .append (includeUpper ? ']' : '}'); return buf.toString(); } /** * compute the intersection of this query with the other, and store the result in this query * @param other another RangeQuery * @return whether the two queries could be merged, which they can if they share the same fieldName and type */ public boolean intersect (RangePQuery other) { if (! (other.fieldName.equals(fieldName) && other.type.equals(type))) { return false; } if (other.lowerTerm != null) { if (lowerTerm == null) { lowerTerm = other.lowerTerm; includeLower = other.includeLower; } else { int cmp = compare(lowerTerm, other.lowerTerm); if (cmp == 0) { includeLower = includeLower && other.includeLower; } else if (cmp < 0) { lowerTerm = other.lowerTerm; includeLower = other.includeLower; } } } if (other.upperTerm != null) { if (upperTerm == null) { upperTerm = other.upperTerm; includeUpper = other.includeUpper; } else { int cmp = compare(upperTerm, other.upperTerm); if (cmp == 0) { includeUpper = includeUpper && other.includeUpper; } else if (cmp > 0) { upperTerm = other.upperTerm; includeUpper = other.includeUpper; } } } return true; } @Override public boolean equals (Object other) { if (other == null) { return false; } if (! (other instanceof RangePQuery)) { return false; } RangePQuery oq = (RangePQuery) other; if (includeLower != oq.includeLower || includeUpper != oq.includeUpper) { return false; } if ((lowerTerm == null) != (oq.lowerTerm == null) || (upperTerm == null) != (oq.upperTerm == null)) { return false; } return ((lowerTerm == null || lowerTerm.equals(oq.lowerTerm)) && ((upperTerm == null) || upperTerm.equals(oq.upperTerm))); } private int compare (String t1, String t2) { if (type.isNumeric) { return Double.valueOf(t1).compareTo(Double.valueOf(t2)); } else { return t1.compareTo(t2); } } @Override public boolean equals(ParseableQuery other) { return equals ((Object) other); } } /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */