http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/OGCFilter.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/OGCFilter.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/OGCFilter.java new file mode 100644 index 0000000..7bf622d --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/OGCFilter.java @@ -0,0 +1,241 @@ +package ss.cloudbase.core.iterators.filter.ogc; + +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import ss.cloudbase.core.iterators.filter.ogc.operation.IOperation; + +import cloudbase.core.data.Key; +import cloudbase.core.data.Value; +import cloudbase.core.iterators.filter.Filter; +import cloudbase.start.classloader.CloudbaseClassLoader; + +/** + * The OGCFilter class provides a basic implementation of the + * <a href="http://www.opengeospatial.org/standards/filter">OGC Filter Encoding specification</a>. + * This allows for arbitrary queries to be passed via XML and executed in a distributed fashion across tablet servers. The following + * code sets up a basic FilteringIterator that uses this filter (note that this jar must be present in each + * of the tablet servers' classpaths to work): + * + * <code> + * <pre> + * cloudbase.core.client.Scanner reader; + * // set up the reader ... + * + * // we're going to parse a row formated like key:value|key:value|key:value and return all of the rows + * // where the key "city" starts with "new" + * String filter = "<PropertyIsLike><PropertyName>city</PropertyName><Literal>new*</Literal></PropertyIsLike>"; + * + * reader.setScanIterators(50, FilteringIterator.class.getName(), "myIterator"); + * reader.setScanIteratorOption("myIterator", "0", OGCFilter.class.getName()); + * reader.setScanIteratorOption("myIterator", "0." + OGCFilter.OPTION_PAIR_DELIMITER, "\\|"); + * reader.setScanIteratorOption("myIterator", "0." + OGCFilter.OPTION_VALUE_DELIMITER, ":"); + * reader.setScanIteratorOption("myIterator", "0." + OGCFilter.OPTION_FILTER, filter); + * <pre> + * </code> + * + * @author William Wall + */ +public class OGCFilter implements Filter { + private static final Logger logger = Logger.getLogger(OGCFilter.class); + + /** The string that separates the key/value pairs in the row value. */ + public static final String OPTION_PAIR_DELIMITER = "pairDelimiter"; + + /** The string that separates the key and the value(s) within a pair in the row value. */ + public static final String OPTION_VALUE_DELIMITER = "valueDelimiter"; + + /** The OGC Filter Encoding XML as a string */ + public static final String OPTION_FILTER = "filter"; + + /** Specifies the column name for the column family. If this option is not included, the column family is not included in the row. */ + public static final String OPTION_COLF_NAME = "colfName"; + + /** Specifies the column name for the column qualifier. If this option is not included, the column qualifier is not included in the row. */ + public static final String OPTION_COLQ_NAME = "colqName"; + + /** Specifies the compare type for the filter. Defaults to "auto". **/ + public static final String OPTION_COMPARE_TYPE = "compareType"; + + public static final String TYPE_NUMERIC = "numeric"; + + public static final String TYPE_STRING = "string"; + + public static final String TYPE_AUTO = "auto"; + + /** Contains the pair delimiter provided through the <code>OPTION_PAIR_DELIMITER</code> option. */ + protected String pairDelimiter; + + /** Contains the value delimiter provided through the <code>OPTION_VALUE_DELIMITER</code> option. */ + protected String valueDelimiter; + + /** Contains the column family column name provided through the <code>OPTION_COLF_NAME</code> option. */ + protected String colfName; + + /** Contains the column qualifier column name provided through the <code>OPTION_COLQ_NAME</code> option. */ + protected String colqName; + + /** The root operation of the query tree **/ + protected IOperation root; + + /** The compare type for the query tree **/ + protected String compareType; + + /** + * Whether or not to accept this key/value entry. A map of row keys and values is parsed and then sent off to the process function to be evaluated. + * @param key The cloudbase entry key + * @param value The cloudbase entry value + * @return True if the entry should be included in the results, false otherwise + */ + @Override + public boolean accept(Key key, Value value) { + if (root != null) { + Map<String, String> row = getRow(key, value); + if (root != null) { + return root.execute(row); + } + } + return false; + } + + public boolean accept(Map<String, String> record) { + if (root != null) { + return root.execute(record); + } + return false; + } + + /** + * Parses the cloudbase value into a map of key/value pairs. If the <code>OPTION_COLF_NAME</code> + * or <code>OPTION_COLQ_NAME</code> options were used, then they will also be added to the row map. + * By default, pairs are delimited by the first unicode character ("\u0000") and values by the last unicode + * character ("\uFFFD"). See the <code>OPTION_PAIR_DELIMITER</code> and <code>OPTION_VALUE_DELIMITER</code> + * options to change these values. + * + * @param cbKey The cloudbase entry key + * @param cbValue The cloudbase entry value + * @return A map that represents this row + */ + protected Map<String, String> getRow(Key cbKey, Value cbValue) { + //TODO: This should really be replaced by CBValueFormatter.parse(value.toString()), but I'm hesitant to require + // more jars (common-data and google-collections) to be in the cloudbase/lib directory. Also, what do we do with + // a field with multiple values? Should we just assume that if any value in that field matches then the row + // matches? Or should they all have to match? + + String value = cbValue.toString(); + Map<String, String> row = new HashMap<String, String>(); + + if (colfName != null) { + row.put(colfName, cbKey.getColumnFamily().toString()); + } + if (colqName != null) { + row.put(colqName, cbKey.getColumnQualifier().toString()); + } + + // Determine the start/end of the value. + int valueStartIndex = 0; + int valueEndIndex = value.length(); + + int vLen = valueDelimiter.length(); + int fLen = pairDelimiter.length(); + + // Parse each of the values from the row value. + while (valueStartIndex < valueEndIndex) { + int vIndex = value.indexOf(valueDelimiter, valueStartIndex); + + // If an "equals" sign was found, parse the key and value. + if (vIndex != -1) { + String key = value.substring(valueStartIndex, vIndex).trim(); + int v = value.indexOf(valueDelimiter, vIndex + vLen); + if (v == -1) { + v = valueEndIndex; + } + int f = value.indexOf(pairDelimiter, vIndex + vLen); + if (f == -1) { + f = valueEndIndex; + } + + int fIndex = Math.min(f,v); + String val = value.substring(vIndex + 1, fIndex).trim(); + valueStartIndex = f; + valueStartIndex += fLen; + row.put(key, val); + } + } + + return row; + } + + @Override + public void init(Map<String, String> options) { + pairDelimiter = options.get(OPTION_PAIR_DELIMITER); + if (pairDelimiter == null || pairDelimiter.length() == 0) { + pairDelimiter = "\u0000"; + } + + valueDelimiter = options.get(OPTION_VALUE_DELIMITER); + if (valueDelimiter == null || valueDelimiter.length() == 0) { + valueDelimiter = "\uFFFD"; + } + + compareType = options.get(OPTION_COMPARE_TYPE); + if (compareType == null || compareType.length() == 0) { + compareType = TYPE_AUTO; + } + + colfName = options.get(OPTION_COLF_NAME); + colqName = options.get(OPTION_COLQ_NAME); + + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(options.get(OPTION_FILTER))); + Document doc = builder.parse(is); + Node filter = doc.getDocumentElement(); + if (filter.getNodeName().equalsIgnoreCase("filter")) { + filter = filter.getFirstChild(); + } + root = createOperationTree(filter); + } catch (IOException e) { + logger.error(e,e); + } catch (SAXException e) { + logger.error(e,e); + } catch (ParserConfigurationException e) { + logger.error(e,e); + } + } + + /** + * Creates the operation tree from the filter XML + * @param node The filter XML node to parse + * @return The root IOperation + */ + protected IOperation createOperationTree(Node node) { + try { + // instantiate the operation and initialize it + Class<? extends IOperation> clazz = CloudbaseClassLoader.loadClass(IOperation.class.getPackage().getName() + "." + node.getNodeName(), IOperation.class); + IOperation op = clazz.newInstance(); + op.init(node, compareType); + return op; + } catch (ClassNotFoundException e) { + logger.warn("Operation not supported: " + node.getNodeName()); + } catch (InstantiationException e) { + logger.error(e,e); + } catch (IllegalAccessException e) { + logger.error(e,e); + } + return null; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractComparisonOp.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractComparisonOp.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractComparisonOp.java new file mode 100644 index 0000000..46b1cf9 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractComparisonOp.java @@ -0,0 +1,80 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import ss.cloudbase.core.iterators.filter.ogc.OGCFilter; + + + +/** + * This class provides a simple init method for setting up most of the variables + * needed to do a comparison operation between two values. + * + * @author William Wall + */ +public abstract class AbstractComparisonOp { + protected String name, literal, value; + protected boolean isNumeric = false; + protected double literalNum, valueNum; + protected String compareType; + + public void init(Node node, String compareType) { + this.compareType = compareType; + Node child; + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + if (child.getNodeName().equalsIgnoreCase("PropertyName")) { + name = child.getTextContent(); + } else { + literal = child.getTextContent(); + } + } + + if (compareType.equalsIgnoreCase(OGCFilter.TYPE_NUMERIC) || compareType.equalsIgnoreCase(OGCFilter.TYPE_AUTO)) { + literalNum = parseNumeric(literal); + isNumeric = !Double.isNaN(literalNum); + } + } + + public List<IOperation> getChildren() { + return null; + } + + protected boolean checkRowNumeric(String s) { + if (isNumeric) { + valueNum = parseNumeric(s); + return valueNum != Double.NaN; + } + return false; + } + + public String getValue(Map<String, String> row) { + String value = row.get(name); + + // nulls will be lexicographically equal to "" + if (value == null) { + value = ""; + } + return value; + } + + public static double parseNumeric(String s) { + // see if the string can be parsed as a double or an integer + double val = Double.NaN; + try { + val = Double.parseDouble(s); + } catch (Exception e) { + try { + val = new Double(Integer.parseInt(s)); + } catch (Exception e2) { + + } + } + return val; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractLogicalOp.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractLogicalOp.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractLogicalOp.java new file mode 100644 index 0000000..0400f61 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/AbstractLogicalOp.java @@ -0,0 +1,40 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import cloudbase.start.classloader.CloudbaseClassLoader; + +public class AbstractLogicalOp { + private static final Logger logger = Logger.getLogger(AbstractLogicalOp.class); + + List<IOperation> children = new ArrayList<IOperation>(); + + public void init(Node node, String compareType) { + Node child; + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + try { + Class<? extends IOperation> clazz = CloudbaseClassLoader.loadClass(IOperation.class.getPackage().getName() + "." + child.getNodeName(), IOperation.class); + IOperation op = clazz.newInstance(); + op.init(child, compareType); + this.children.add(op); + } catch (ClassNotFoundException e) { + logger.warn("Operation not supported: " + node.getNodeName()); + } catch (InstantiationException e) { + logger.error(e,e); + } catch (IllegalAccessException e) { + logger.error(e,e); + } + } + } + + public List<IOperation> getChildren() { + return children; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/And.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/And.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/And.java new file mode 100644 index 0000000..b192b19 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/And.java @@ -0,0 +1,29 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * Executes a logical AND on all the child operations. + * + * <code> + * <pre> + * <And> + * <PropertyIsEqualTo>... + * <PropertyIsLessThan>... + * </And> + * </pre> + * </code> + * + * @author William Wall + */ +public class And extends AbstractLogicalOp implements IOperation { + @Override + public boolean execute(Map<String, String> row) { + boolean result = true; + for (int i = 0; i < children.size(); i++) { + result = children.get(i).execute(row); + if (!result) break; + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/BBOX.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/BBOX.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/BBOX.java new file mode 100644 index 0000000..8596338 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/BBOX.java @@ -0,0 +1,125 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Point2D; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * Tests a row to see if it falls within the given shape. The shape should be + * defined in degrees. There is no need to send a property name since all the + * rows contain either lonself/latself or lon/lat fields. + * + * Example: + * <pre> + * <BBOX> + * <gml:Envelope> + * <!-- coordinates are in lon/lat order --> + * <gml:LowerCorner>13.09 31.5899</gml:LowerCorner> + * <gml:UpperCorner>35.725 42.8153</gml:UpperCorner> + * </gml:Envelope> + * </BBOX> + * </pre> + * + * @author William Wall + */ +public class BBOX implements IOperation { + private static final Logger logger = Logger.getLogger(BBOX.class); + + Shape shape; + + // longitude column names in order of priority + protected static final String[] LON_NAMES = { + "lonself", + "lon", + "long", + "longitude" + }; + + // latitude column names in order of priority + protected static final String[] LAT_NAMES = { + "latself", + "lat", + "latitude" + }; + + @Override + public boolean execute(Map<String, String> row) { + Point2D p = BBOX.getPoint(row); + + if (p != null && shape != null) { + if (shape.contains(p)) { + return true; + } else { + // attempt to normalize the point into the shape in the event that the shape + // bounds are outside of -180 to 180 + Rectangle bounds = shape.getBounds(); + while (p.getX() < bounds.getMinX()) { + p.setLocation(p.getX() + 360, p.getY()); + } + while (p.getX() > bounds.getMaxX()) { + p.setLocation(p.getX() - 360, p.getY()); + } + + return shape.contains(p); + } + } + + return false; + } + + @Override + public List<IOperation> getChildren() { + return null; + } + + @Override + public void init(Node node, String compareType) { + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + shape = ShapeFactory.getShape(children.item(i)); + if (shape != null) { + break; + } + } + + } + + /** + * Gets a point that represents the location of this row. See + * @param row + * @return The point object as (x - Longitude, y - Latitude) + */ + protected static Point2D getPoint(Map<String, String> row) { + Point2D.Double p = new Point2D.Double(); + p.x = getDegree(row, LON_NAMES); + p.y = getDegree(row, LAT_NAMES); + return p; + } + + protected static double getDegree(Map<String, String> row, String[] cols) { + double num = Double.NaN; + String value; + for (int i = 0; i < cols.length; i++) { + if (row.containsKey(cols[i])) { + value = row.get(cols[i]); + if (value != null && !value.equals("-")) { + try { + num = Double.parseDouble(value); + break; + } catch (NumberFormatException e) { + logger.warn("Could not parse degree value from " + cols[i] + " = " + value); + } + } + } + } + + return num; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/IOperation.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/IOperation.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/IOperation.java new file mode 100644 index 0000000..6ad8ebe --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/IOperation.java @@ -0,0 +1,30 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Node; + +public interface IOperation { + + /** + * Sets up the operation from the filter XML node + * @param node The node + * @param compareType The compare type. Defaults to "auto", but you can force it to be "numeric" or "string". + */ + public void init(Node node, String compareType); + + /** + * Executes the operation indicated by the given node in the query + * tree. + * @param row The key/value pairs for the current row + * @return The boolean evaluation of the operation + */ + public boolean execute(Map<String, String> row); + + /** + * Returns the nodes children. This is only applicable to logical + * operations (AND, OR, NOT). + */ + public List<IOperation> getChildren(); +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Not.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Not.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Not.java new file mode 100644 index 0000000..74826a8 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Not.java @@ -0,0 +1,35 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * Executes a logical NOT on the child operations. If there is a single child, then + * the operation is NOT. If more than one child exists, this operation defaults to + * NOR behavior. For NAND behavior, make a single AND child of NOT. + * + * <code> + * <pre> + * <Not> + * <PropertyIsEqualTo>... + * </Not> + * </pre> + * </code> + * + * @author William Wall + * + */ +public class Not extends AbstractLogicalOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + // For typical NOT behavior, a NOT group should have one child. If it has more than one child, it behaves + // like NOR. NAND/NOR behavior can be implemented by giving the Not group a child group of AND/OR. + boolean result = true; + for (int i = 0; i < children.size(); i++) { + result = !children.get(i).execute(row); + // in the case that there are multiple children, treat them as NOR + if (!result) break; + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Or.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Or.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Or.java new file mode 100644 index 0000000..0a3dd6e --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/Or.java @@ -0,0 +1,29 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * Executes a logical OR on the child operations. + * + * <code> + * <pre> + * <Or> + * <PropertyIsEqualTo>... + * <PropertyIsLessThan>... + * </Or> + * </pre> + * </code> + * + * @author William Wall + */ +public class Or extends AbstractLogicalOp implements IOperation { + @Override + public boolean execute(Map<String, String> row) { + boolean result = false; + for (int i = 0; i < children.size(); i++) { + result = children.get(i).execute(row); + if (result) break; + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsBetween.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsBetween.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsBetween.java new file mode 100644 index 0000000..2c9d86c --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsBetween.java @@ -0,0 +1,76 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import ss.cloudbase.core.iterators.filter.ogc.OGCFilter; + + +/** + * An operation that determines if the row's value is between the given + * boundary values. + * + * Example: + * <pre> + * <PropertyIsBetween> + * <PropertyName>height</PropertyName> + * <LowerBoundary><Literal>180</Literal></LowerBoundary> + * <UpperBoundary><Literal>200</Literal></UpperBoundary> + * </PropertyIsBetween> + * </pre> + * + * @author William Wall + */ +public class PropertyIsBetween implements IOperation { + String name; + String lower, upper, value; + double lowerNum, upperNum, valueNum; + boolean isNumeric = false; + + @Override + public boolean execute(Map<String, String> row) { + if (isNumeric) { + valueNum = AbstractComparisonOp.parseNumeric(row.get(name)); + if (valueNum != Double.NaN) { + return lowerNum <= valueNum && valueNum <= upperNum; + } + } + + value = row.get(name); + if (value == null) { + value = ""; + } + + return value.compareTo(lower) > -1 && value.compareTo(upper) < 1; + } + + @Override + public List<IOperation> getChildren() { + return null; + } + + @Override + public void init(Node node, String compareType) { + Node child; + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + if (child.getNodeName().equalsIgnoreCase("PropertyName")) { + name = child.getTextContent(); + } else if (child.getNodeName().equalsIgnoreCase("LowerBoundary")) { + lower = child.getTextContent(); + } else if (child.getNodeName().equalsIgnoreCase("UpperBoundary")) { + upper = child.getTextContent(); + } + } + + if (compareType.equalsIgnoreCase(OGCFilter.TYPE_NUMERIC) || compareType.equalsIgnoreCase(OGCFilter.TYPE_AUTO)) { + upperNum = AbstractComparisonOp.parseNumeric(upper); + lowerNum = AbstractComparisonOp.parseNumeric(lower); + isNumeric = !Double.isNaN(upperNum) && !Double.isNaN(lowerNum); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsEqualTo.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsEqualTo.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsEqualTo.java new file mode 100644 index 0000000..93fa3f5 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsEqualTo.java @@ -0,0 +1,30 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation to see whether the values are equal or not. + * + * Example: + * <pre> + * <PropertyIsEqualTo> + * <PropertyName>user</PropertyIsEqualTo> + * <Literal>CmdrTaco</Literal> + * </PropertyIsEqualTo> + * </pre> + * + * @author William Wall + */ +public class PropertyIsEqualTo extends AbstractComparisonOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum == literalNum; + } + + return value.equals(literal); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThan.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThan.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThan.java new file mode 100644 index 0000000..8eb9ec0 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThan.java @@ -0,0 +1,29 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation to see if the row value is greater than the given value. + * + * Example: + * <pre> + * <PropertyIsGreaterThan> + * <PropertyName>height</PropertyName> + * <Literal>200</Literal> + * </PropertyIsGreaterThan> + * </pre> + * @author William Wall + */ +public class PropertyIsGreaterThan extends AbstractComparisonOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum > literalNum; + } + + return value.compareTo(literal) > 0; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThanOrEqualTo.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThanOrEqualTo.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThanOrEqualTo.java new file mode 100644 index 0000000..17fb1dc --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsGreaterThanOrEqualTo.java @@ -0,0 +1,29 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation to see if the row value is greater than or equal to the given value. + * + * Example: + * <pre> + * <PropertyIsGreaterThanOrEqualTo> + * <PropertyName>height</PropertyName> + * <Literal>100</Literal> + * </PropertyIsGreaterThanOrEqualTo> + * </pre> + * @author William Wall + */ +public class PropertyIsGreaterThanOrEqualTo extends AbstractComparisonOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum >= literalNum; + } + + return value.compareTo(literal) > -1; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThan.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThan.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThan.java new file mode 100644 index 0000000..f094c99 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThan.java @@ -0,0 +1,31 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation to see if the row value is less than the given value. + * + * Example: + * <pre> + * <PropertyIsLessThan> + * <PropertyName>height</PropertyName> + * <Literal>180</Literal> + * </PropertyIsLessThan> + * </pre> + * + * @author William Wall + */ +public class PropertyIsLessThan extends AbstractComparisonOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum < literalNum; + } + + return value.compareTo(literal) < 0; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThanOrEqualTo.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThanOrEqualTo.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThanOrEqualTo.java new file mode 100644 index 0000000..9b01aae --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLessThanOrEqualTo.java @@ -0,0 +1,29 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation to see if the row value is less than or equal to the given value. + * + * <pre> + * <PropertyIsLessThanOrEqualTo> + * <PropertyName>height</PropertyName> + * <Literal>100</Literal> + * </PropertyIsLessThanOrEqualTo> + * </pre> + * + * @author William Wall + */ +public class PropertyIsLessThanOrEqualTo extends AbstractComparisonOp implements IOperation { + + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum <= literalNum; + } + + return value.compareTo(literal) < 1; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLike.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLike.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLike.java new file mode 100644 index 0000000..ad38951 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsLike.java @@ -0,0 +1,144 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * An operation that determines if the row value is like the given value. This + * operation supports wildcards (*). + * + * Example: + * + * <pre> + * <PropertyIsLike> + * <PropertyName>city</PropertyName> + * <Literal>new*</Literal> + * </PropertyIsLike> + * </pre> + * + * @author William Wall + * + */ +public class PropertyIsLike implements IOperation { + String pattern; + String name; + + @Override + public boolean execute(Map<String, String> row) { + String value = row.get(name); + if (value == null) { + value = ""; + } + + return value.matches(pattern); + } + + @Override + public List<IOperation> getChildren() { + return null; + } + + @Override + public void init(Node node, String compareType) { + Node child; + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + if (child.getNodeName().equalsIgnoreCase("PropertyName")) { + name = child.getTextContent(); + } else { + pattern = child.getTextContent(); + } + } + + pattern = convertToRegex(node, pattern); + } + + /** + * Converts the pattern, wild card, single and escape characters to the + * regular expression equivalents. Everything else in the pattern is treated + * as a regex literal. + * + * @param node The PropertyIsLike node + * @param likePattern The initial like pattern + * + * @return the equivalent regular expression string. + */ + public String convertToRegex(Node node, String likePattern) { + // Convert the pattern to a regular expression. + StringBuilder regex = new StringBuilder(); + + NamedNodeMap attr = node.getAttributes(); + + String wildCard = "*"; + if (attr.getNamedItem("wildCard") != null) { + wildCard = attr.getNamedItem("wildCard").toString(); + } + + String escapeChar = "\\"; + if (attr.getNamedItem("escapeChar") != null) { + escapeChar = attr.getNamedItem("escapeChar").toString(); + } + + String singleChar = "."; + if (attr.getNamedItem("singleChar") != null) { + singleChar = attr.getNamedItem("singleChar").toString(); + } + + int escapeCharIndex = likePattern.indexOf(escapeChar); + + // These are required in WFS but we'll handle null values here. + int wildCardIndex = wildCard == null ? -1 : likePattern.indexOf(wildCard); + int singleCharIndex = singleChar == null ? -1 : likePattern.indexOf(singleChar); + for (int index = 0; index < likePattern.length(); index++) { + char ch = likePattern.charAt(index); + if (index == escapeCharIndex) { + escapeCharIndex = likePattern.indexOf(escapeChar, escapeCharIndex + escapeChar.length()); + + // If there are consecutive escape characters, skip to the + // next one to save it in the regex. + if (index + 1 == escapeCharIndex) { + escapeCharIndex = likePattern.indexOf(escapeChar, escapeCharIndex + escapeChar.length()); + } else if (index + 1 == wildCardIndex) { + wildCardIndex = likePattern.indexOf(wildCard, wildCardIndex + wildCard.length()); + } else if (index + 1 == singleCharIndex) { + singleCharIndex = likePattern.indexOf(singleChar, singleCharIndex + singleChar.length()); + } else { + // This is an undefined condition, just skip the escape + // character. + } + } + + // Insert the regular expression equivalent of a wild card. + else if (index == wildCardIndex) { + regex.append(".*"); + index += wildCard.length() - 1; + wildCardIndex = likePattern.indexOf(wildCard, wildCardIndex + wildCard.length()); + } + + // Insert the regular expression equivalent of the single char. + else if (index == singleCharIndex) { + regex.append("."); + index += singleChar.length() - 1; + singleCharIndex = likePattern.indexOf(singleChar, singleCharIndex + singleChar.length()); + } + + // Handle certain characters in a special manner. + else if (('[' == ch) || (']' == ch) || ('\\' == ch) || ('^' == ch)) { + regex.append('\\').append(ch); + } + + // Force everything else to be literals. + else { + regex.append('[').append(ch).append(']'); + } + } + + // add case insensitive flag and start match at beginning of the string + return "(?i)^" + regex.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNotEqualTo.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNotEqualTo.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNotEqualTo.java new file mode 100644 index 0000000..b62e6c6 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNotEqualTo.java @@ -0,0 +1,30 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.Map; + +/** + * An operation that determines if the row value is not equal to the given value. + * + * Example: + * <pre> + * <PropertyIsNotEqualTo> + * <PropertyName>weather</PropertyName> + * <Literal>rainy</Literal> + * </PropertyIsNotEqualTo> + * </pre> + * + * @author William Wall + * + */ +public class PropertyIsNotEqualTo extends AbstractComparisonOp implements IOperation { + @Override + public boolean execute(Map<String, String> row) { + value = getValue(row); + + if (checkRowNumeric(value)) { + return valueNum != literalNum; + } + + return !value.equals(literal); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNull.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNull.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNull.java new file mode 100644 index 0000000..d5d67c2 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/PropertyIsNull.java @@ -0,0 +1,38 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Node; + +/** + * An operation to determine if the row value is null. Nulls and empty strings will both match. + * + * Example: + * <pre> + * <PropertyIsNull> + * <PropertyName>socialSkills</PropertyName> + * </PropertyIsNull> + * </pre> + * + * @author William Wall + */ +public class PropertyIsNull implements IOperation { + String name; + + @Override + public boolean execute(Map<String, String> row) { + String value = row.get(name); + return value == null || value.length() == 0; + } + + @Override + public List<IOperation> getChildren() { + return null; + } + + @Override + public void init(Node node, String compareType) { + name = node.getTextContent(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/ShapeFactory.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/ShapeFactory.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/ShapeFactory.java new file mode 100644 index 0000000..10fea78 --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/operation/ShapeFactory.java @@ -0,0 +1,133 @@ +package ss.cloudbase.core.iterators.filter.ogc.operation; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import org.apache.log4j.Logger; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import ss.cloudbase.core.iterators.filter.ogc.util.GeoUtil; + + +public class ShapeFactory { + private static final Logger logger = Logger.getLogger(ShapeFactory.class); + + public static Shape getShape(Node node) { + if (node.getNodeName().equalsIgnoreCase("gml:Envelope")) { + return parseEnvelope(node); + } else if (node.getNodeName().equalsIgnoreCase("gml:Polygon")) { + return parsePolygon(node); + } else if (node.getNodeName().equalsIgnoreCase("gml:CircleByCenterPoint")) { + return parseCircle(node); + } + + logger.warn("No parser implemented for: " + node.getLocalName()); + return null; + } + + protected static Shape parseEnvelope(Node node) { + Rectangle rect = null; + + Node child; + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + String[] parts = child.getTextContent().split("\\s"); + + if (parts.length == 2) { + double lon = Double.parseDouble(parts[0]); + double lat = Double.parseDouble(parts[1]); + + if (rect == null) { + rect = new Rectangle(); + rect.setFrame(lon, lat, 0, 0); + } else { + rect.add(lon, lat); + } + } + } + + // If the rectangle width is greater than 180 degrees, the user most likely + // meant to use the inverse BBOX (where the east value is less than the west). + // This is for clients that wrap coordinates rather than use absolute coordinates. + if (rect.getWidth() > 180) { + rect.setFrame(rect.getMaxX(), rect.getMaxY(), 360 - rect.getWidth(), rect.getHeight()); + } + + return rect; + } + + protected static Shape parsePolygon(Node node) { + Path2D poly = null; + + String text = node.getTextContent(); + String[] list = text.split("\\s"); + + for (int i = 1; i < list.length; i += 2) { + double lon = Double.parseDouble(list[i-1]); + double lat = Double.parseDouble(list[i]); + if (poly == null) { + poly = new Path2D.Double(); + poly.moveTo(lon, lat); + } else { + poly.lineTo(lon, lat); + } + } + + return poly; + } + + protected static Shape parseCircle(Node node) { + Ellipse2D circle = null; + + double radius = Double.NaN, lon = Double.NaN, lat = Double.NaN; + String units = null; + + Node child; + NodeList children = node.getChildNodes(); + try { + for (int i = 0; i < children.getLength(); i++) { + child = children.item(i); + if (child.getNodeName().equalsIgnoreCase("gml:radius")) { + radius = Double.parseDouble(child.getTextContent()); + units = child.getAttributes().getNamedItem("uom").getTextContent(); + } else { + String[] list = child.getTextContent().split("\\s"); + lon = Double.parseDouble(list[0]); + lat = Double.parseDouble(list[1]); + } + } + + radius = convertToKM(radius, units); + Point2D center = new Point2D.Double(lon, lat); + Point2D end = GeoUtil.calculateEndLocation(center, radius, lat > 0 ? 180: 0); + + radius = Math.abs(end.getY() - lat); + circle = new Ellipse2D.Double(); + circle.setFrameFromCenter(center, new Point2D.Double(center.getX() + radius, center.getY() + radius)); + } catch (NumberFormatException e) { + + } catch (ArrayIndexOutOfBoundsException e) { + + } + + return circle; + } + + private static double convertToKM(double radius, String units) { + if (units.equalsIgnoreCase("km")) { + return radius; + } else if (units.equalsIgnoreCase("m")) { + return radius / 1000; + } else if (units.equalsIgnoreCase("mi")) { + return 0.621371192 * radius; + } else if (units.equalsIgnoreCase("ft")) { + return radius / 3280.8399; + } + return radius; + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/util/GeoUtil.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/util/GeoUtil.java b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/util/GeoUtil.java new file mode 100644 index 0000000..95259fa --- /dev/null +++ b/partition/common-query/src/main/java/ss/cloudbase/core/iterators/filter/ogc/util/GeoUtil.java @@ -0,0 +1,32 @@ +package ss.cloudbase.core.iterators.filter.ogc.util; + +import java.awt.geom.Point2D; + +public class GeoUtil { + /** + * Calculates an ending location from a point, distance, and bearing + * @param point The start point + * @param distance The distance from the start point in kilometers + * @param bearing The bearing (in degrees) where north is 0 + * @return The resulting point + */ + public static Point2D calculateEndLocation(Point2D point, double distance, double bearing) { + double r = 6371; // earth's mean radius in km + + double lon1 = Math.toRadians(point.getX()); + double lat1 = Math.toRadians(point.getY()); + bearing = Math.toRadians(bearing); + + double lat2 = Math.asin( Math.sin(lat1) * Math.cos(distance/r) + Math.cos(lat1) * Math.sin(distance/r) * Math.cos(bearing) ); + double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance/r) * Math.cos(lat1), Math.cos(distance/r) - Math.sin(lat1) * Math.sin(lat2)); + + lon2 = (lon2+Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180 + + if (Double.isNaN(lat2) || Double.isNaN(lon2)) return null; + + lon2 = Math.toDegrees(lon2); + lat2 = Math.toDegrees(lat2); + + return new Point2D.Double(lon2, lat2); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/test/java/GVDateFilterTest.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/test/java/GVDateFilterTest.java b/partition/common-query/src/test/java/GVDateFilterTest.java new file mode 100644 index 0000000..8ea5578 --- /dev/null +++ b/partition/common-query/src/test/java/GVDateFilterTest.java @@ -0,0 +1,156 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.hadoop.io.Text; +import org.junit.Test; + +import ss.cloudbase.core.iterators.GMDenIntersectingIterator; +import ss.cloudbase.core.iterators.filter.general.GVDateFilter; + +import cloudbase.core.client.Connector; +import cloudbase.core.client.Scanner; +import cloudbase.core.client.TableNotFoundException; +import cloudbase.core.data.Key; +import cloudbase.core.data.Range; +import cloudbase.core.data.Value; +import cloudbase.core.iterators.FilteringIterator; +import cloudbase.core.security.Authorizations; + +/** + * + * @author rashah + */ +public class GVDateFilterTest +{ + + private Connector cellLevelConn; + private Connector serializedConn; + private static final String TABLE = "partition"; + private static final Authorizations AUTHS = new Authorizations("ALPHA,BETA,GAMMA".split(",")); + + + + protected Connector getSerializedConnector() + { + if (serializedConn == null) + { + serializedConn = SampleGVData.initConnector(); + SampleGVData.writeDenSerialized(serializedConn, SampleGVData.sampleData()); + } + return serializedConn; + } + + + + protected Scanner getSerializedScanner() + { + Connector c = getSerializedConnector(); + try + { + return c.createScanner(TABLE, AUTHS); + } + catch (TableNotFoundException e) + { + return null; + } + } + + protected Scanner setUpGVDFFilter(Scanner s, String timesta) + { + try + { + + s.setScanIterators(50, FilteringIterator.class.getName(), "gvdf"); + s.setScanIteratorOption("gvdf", "0", GVDateFilter.class.getName()); + s.setScanIteratorOption("gvdf", "0." + GVDateFilter.OPTIONInTimestamp, timesta); + + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return s; + } + + protected String checkSerialized(Scanner s) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry<Key, Value> e : s) + { + + if (!first) + { + sb.append(","); + } + else + { + first = false; + } + + String colq = e.getKey().getColumnQualifier().toString(); + + sb.append(colq); + } + return sb.toString(); + } + + + @Test + public void testNoResults() + { + + Scanner s = setUpGVDFFilter(getSerializedScanner(), "2008-03-03T20:44:28.633Z"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).equals("")); + } + + + @Test + public void testOneResult() + { + + Scanner s = setUpGVDFFilter(getSerializedScanner(), "2011-03-03T20:44:28.633Z"); + s.setRange(new Range()); + + System.out.println(checkSerialized(s)); + + assertTrue(checkSerialized(s).equals("03")); + } + + @Test + public void testTwoResults() + { + + Scanner s = setUpGVDFFilter(getSerializedScanner(), "2009-03-03T20:44:28.633Z"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).equals("04,01")); + } + + @Test + public void testThreeResults() + { + + Scanner s = setUpGVDFFilter(getSerializedScanner(), "2010-03-01T20:44:28.633Z"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).equals("04,01,03")); + } + + @Test + public void testDummyTest() + { + assertTrue(true); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/test/java/GVFrequencyFilterTest.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/test/java/GVFrequencyFilterTest.java b/partition/common-query/src/test/java/GVFrequencyFilterTest.java new file mode 100644 index 0000000..25c602a --- /dev/null +++ b/partition/common-query/src/test/java/GVFrequencyFilterTest.java @@ -0,0 +1,144 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.hadoop.io.Text; +import org.junit.Test; + +import ss.cloudbase.core.iterators.GMDenIntersectingIterator; +import ss.cloudbase.core.iterators.filter.general.GVFrequencyFilter; + +import cloudbase.core.client.Connector; +import cloudbase.core.client.Scanner; +import cloudbase.core.client.TableNotFoundException; +import cloudbase.core.data.Key; +import cloudbase.core.data.Range; +import cloudbase.core.data.Value; +import cloudbase.core.iterators.FilteringIterator; +import cloudbase.core.security.Authorizations; + +/** + * + * @author rashah + */ +public class GVFrequencyFilterTest +{ + + private Connector cellLevelConn; + private Connector serializedConn; + private static final String TABLE = "partition"; + private static final Authorizations AUTHS = new Authorizations("ALPHA,BETA,GAMMA".split(",")); + + + + protected Connector getSerializedConnector() + { + if (serializedConn == null) + { + serializedConn = SampleGVData.initConnector(); + SampleGVData.writeDenSerialized(serializedConn, SampleGVData.sampleData()); + } + return serializedConn; + } + + + + protected Scanner getSerializedScanner() + { + Connector c = getSerializedConnector(); + try + { + return c.createScanner(TABLE, AUTHS); + } + catch (TableNotFoundException e) + { + return null; + } + } + + protected Scanner setUpGVDFFilter(Scanner s, String Frequency) + { + try + { + s.clearScanIterators(); + + s.setScanIterators(50, FilteringIterator.class.getName(), "gvff"); + s.setScanIteratorOption("gvff", "0", GVFrequencyFilter.class.getName()); + s.setScanIteratorOption("gvff", "0." + GVFrequencyFilter.OPTIONFrequency, Frequency); + + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return s; + } + + protected String checkSerialized(Scanner s) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry<Key, Value> e : s) + { + + if (!first) + { + sb.append(","); + } + else + { + first = false; + } + + String colq = e.getKey().getColumnQualifier().toString(); + + //System.out.println(e.getKey()+"\t"+e.getValue()); + + sb.append(colq); + } + return sb.toString(); + } + + @Test + public void testNoMatch() + { + + Scanner s = setUpGVDFFilter(getSerializedScanner(), "2000000000"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).isEmpty()); + } + + @Test + public void testSingleMatch() + { + Scanner s = setUpGVDFFilter(getSerializedScanner(), "1500000000"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).equals("01")); + } + + + @Test + public void testDoubleMatch() + { + Scanner s = setUpGVDFFilter(getSerializedScanner(), "1200000000"); + s.setRange(new Range()); + + assertTrue(checkSerialized(s).equals("01,03")); + } + + @Test + public void testDummyTest() + { + assertTrue(true); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/test/java/IteratorTest.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/test/java/IteratorTest.java b/partition/common-query/src/test/java/IteratorTest.java new file mode 100644 index 0000000..1b5cf14 --- /dev/null +++ b/partition/common-query/src/test/java/IteratorTest.java @@ -0,0 +1,554 @@ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.hadoop.io.Text; +import org.junit.Test; + +import ss.cloudbase.core.iterators.CellLevelFilteringIterator; +import ss.cloudbase.core.iterators.CellLevelRecordIterator; +import ss.cloudbase.core.iterators.ConversionIterator; +import ss.cloudbase.core.iterators.GMDenIntersectingIterator; +import ss.cloudbase.core.iterators.SortedMinIterator; +import ss.cloudbase.core.iterators.SortedRangeIterator; +import ss.cloudbase.core.iterators.UniqueIterator; +import ss.cloudbase.core.iterators.filter.CBConverter; +import cloudbase.core.client.Connector; +import cloudbase.core.client.Scanner; +import cloudbase.core.client.TableNotFoundException; +import cloudbase.core.data.Key; +import cloudbase.core.data.PartialKey; +import cloudbase.core.data.Range; +import cloudbase.core.data.Value; +import cloudbase.core.security.Authorizations; + +public class IteratorTest { + private Connector cellLevelConn; + private Connector serializedConn; + + private static final String TABLE = "partition"; + private static final Authorizations AUTHS = new Authorizations("ALPHA,BETA,GAMMA".split(",")); + + public IteratorTest() { + + } + + protected Connector getCellLevelConnector() { + if (cellLevelConn == null) { + cellLevelConn = SampleData.initConnector(); + SampleData.writeDenCellLevel(cellLevelConn, SampleData.sampleData()); + } + return cellLevelConn; + } + + protected Connector getSerializedConnector() { + if (serializedConn == null) { + serializedConn = SampleData.initConnector(); + SampleData.writeDenSerialized(serializedConn, SampleData.sampleData()); + SampleData.writeDenProvenance(serializedConn); + SampleData.writeMinIndexes(serializedConn); + } + return serializedConn; + } + + protected Scanner getProvenanceScanner() { + Connector c = getSerializedConnector(); + try { + return c.createScanner("provenance", AUTHS); + } catch (TableNotFoundException e) { + return null; + } + } + + protected Scanner getCellLevelScanner() { + Connector c = getCellLevelConnector(); + try { + return c.createScanner(TABLE, AUTHS); + } catch (TableNotFoundException e) { + return null; + } + } + + protected Scanner getSerializedScanner() { + Connector c = getSerializedConnector(); + try { + return c.createScanner(TABLE, AUTHS); + } catch (TableNotFoundException e) { + return null; + } + } + + protected Scanner setUpIntersectingIterator(Scanner s, Text[] terms, boolean multiDoc) { + try { + s.setScanIterators(50, GMDenIntersectingIterator.class.getName(), "ii"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + s.setScanIteratorOption("ii", GMDenIntersectingIterator.indexFamilyOptionName, "index"); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.docFamilyOptionName, "event"); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.OPTION_MULTI_DOC, "" + multiDoc); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.columnFamiliesOptionName, GMDenIntersectingIterator.encodeColumns(terms)); + return s; + } + + protected String checkSerialized(Scanner s) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry<Key, Value> e: s) { + if (!first) { + sb.append(","); + } else { + first = false; + } + + String colq = e.getKey().getColumnQualifier().toString(); + + sb.append(colq); + } + return sb.toString(); + } + + protected String checkCellLevel(Scanner s) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry<Key, Value> e: s) { + String colq = e.getKey().getColumnQualifier().toString(); + int i = colq.indexOf("\u0000"); + if (i > -1) { + if (!first) { + sb.append(","); + } else { + first = false; + } + sb.append(colq.substring(0, i)); + sb.append("."); + sb.append(colq.substring(i + 1)); + sb.append("="); + sb.append(e.getValue().toString()); + } + } + return sb.toString(); + } + + @Test + public void testSerializedSingleDuplicate() { + Text[] terms = new Text[] { + new Text("A"), + new Text("A") + }; + + String test = "01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testCellLevelSingleDuplicate() { + Text[] terms = new Text[] { + new Text("A"), + new Text("A") + }; + String test = "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E"; + Scanner s = setUpIntersectingIterator(getCellLevelScanner(), terms, true); + s.setRange(new Range()); + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testSerializedTwoTerms() { + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + // all the evens will come first + String test = "02,01,03"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testCellLevelTwoTerms() { + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + + String test = "02.field0=B,02.field1=C,02.field2=D,02.field3=E,02.field4=F," + + "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E," + + "03.field0=C,03.field1=D,03.field2=E,03.field3=F,03.field4=G"; + Scanner s = setUpIntersectingIterator(getCellLevelScanner(), terms, true); + s.setRange(new Range()); + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testSerializedTwoTermsWithRange() { + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + + String test = "02"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range(new Key(new Text("0")), true, new Key(new Text("1")), false)); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testCellLevelTwoTermsWithRange() { + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + + String test = "02.field0=B,02.field1=C,02.field2=D,02.field3=E,02.field4=F"; + Scanner s = setUpIntersectingIterator(getCellLevelScanner(), terms, true); + s.setRange(new Range(new Key(new Text("0")), true, new Key(new Text("1")), false)); + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testSerializedSingleRange() { + Text[] terms = new Text[] { + new Text(GMDenIntersectingIterator.getRangeTerm("index", "A", true, "B", true)), + new Text(GMDenIntersectingIterator.getRangeTerm("index", "A", true, "B", true)) + }; + + String test = "02,01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testSerializedMultiRange() { + Text[] terms = new Text[] { + new Text(GMDenIntersectingIterator.getRangeTerm("index", "A", true, "B", true)), + new Text(GMDenIntersectingIterator.getRangeTerm("index", "B", true, "C", true)) + }; + + String test = "02,01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testSerializedTermAndRange() { + Text[] terms = new Text[] { + new Text("B"), + new Text(GMDenIntersectingIterator.getRangeTerm("index", "A", true, "E", true)) + }; + + String test = "02,01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + protected Scanner setUpSortedRangeIterator(Scanner s, boolean multiDoc) { + try { + s.setScanIterators(50, SortedRangeIterator.class.getName(), "ri"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_COLF, "index"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_DOC_COLF, "event"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_LOWER_BOUND, "A"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_UPPER_BOUND, "C"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_START_INCLUSIVE, "true"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_END_INCLUSIVE, "true"); + s.setScanIteratorOption("ri", SortedRangeIterator.OPTION_MULTI_DOC, "" + multiDoc); + return s; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Test + public void testSerializedSortedRangeIterator() { + Scanner s = setUpSortedRangeIterator(getSerializedScanner(), false); + String test = "02,01,03"; + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testCellLevelSortedRangeIterator() { + Scanner s = setUpSortedRangeIterator(getCellLevelScanner(), true); + String test = "02.field0=B,02.field1=C,02.field2=D,02.field3=E,02.field4=F," + + "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E," + + "03.field0=C,03.field1=D,03.field2=E,03.field3=F,03.field4=G"; + s.setRange(new Range()); + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testUniqueIterator() { + Scanner s = getProvenanceScanner(); + try { + s.setScanIterators(50, UniqueIterator.class.getName(), "skipper"); + Key start = new Key(new Text("sid1")); + s.setRange(new Range(start, start.followingKey(PartialKey.ROW))); + + int count = 0; + for (Entry<Key, Value> e: s) { + count++; + } + + assertEquals(count, 3); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected Scanner setUpConversionIterator(Scanner s) { + String[] conversions = new String[] { + "field0 + 10", + "field1 - 10", + "field2 * 10", + "field3 / 10", + "field4 % 10" + }; + + try { + s.setScanIterators(50, ConversionIterator.class.getName(), "ci"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + s.setScanIteratorOption("ci", ConversionIterator.OPTION_CONVERSIONS, ConversionIterator.encodeConversions(conversions)); + Key start = new Key(new Text("1"), new Text("event"), new Text("01")); + s.setRange(new Range(start, true, start.followingKey(PartialKey.ROW_COLFAM_COLQUAL), false)); + + return s; + } + + @Test + public void testConversionIteratorSerialized() { + Scanner s = getSerializedScanner(); + s = setUpConversionIterator(s); + + CBConverter c = new CBConverter(); + + boolean test = true; + Map<String, Double> expected = new HashMap<String, Double>(); + + expected.put("field0", 20.0); + expected.put("field1", 1.0); + expected.put("field2", 120.0); + expected.put("field3", 1.3); + expected.put("field4", 4.0); + + Map<String, String> record; + + for (Entry<Key, Value> e: s) { + record = c.toMap(e.getKey(), e.getValue()); + + for (Entry<String, String> pair: record.entrySet()) { + test = test && expected.get(pair.getKey()).equals(new Double(Double.parseDouble(record.get(pair.getKey())))); + } + } + + assertTrue(test); + } + + @Test + public void testConversionIteratorCellLevel() { + Scanner s = getCellLevelScanner(); + s = setUpConversionIterator(s); + s.setScanIteratorOption("ci", ConversionIterator.OPTION_MULTI_DOC, "true"); + + boolean test = true; + Map<String, Double> expected = new HashMap<String, Double>(); + + expected.put("field0", 20.0); + expected.put("field1", 1.0); + expected.put("field2", 120.0); + expected.put("field3", 1.3); + expected.put("field4", 4.0); + + for (Entry<Key, Value> e: s) { + String field = getField(e.getKey()); + if (field != null) { + test = test && expected.get(field).equals(new Double(Double.parseDouble(e.getValue().toString()))); + } + } + + assertTrue(test); + } + + protected String getField(Key key) { + String colq = key.getColumnQualifier().toString(); + int start = colq.indexOf("\u0000"); + if (start == -1) { + return null; + } + + int end = colq.indexOf("\u0000", start + 1); + if (end == -1) { + end = colq.length(); + } + + return colq.substring(start + 1, end); + } + + @Test + public void testCellLevelOGCFilter() { + Scanner s = getCellLevelScanner(); + s.fetchColumnFamily(new Text("event")); + + try { + s.setScanIterators(60, CellLevelFilteringIterator.class.getName(), "fi"); + } catch (IOException e) { + e.printStackTrace(); + } + + s.setScanIteratorOption("fi", CellLevelFilteringIterator.OPTION_FILTER, "<PropertyIsBetween><PropertyName>field0</PropertyName>" + + "<LowerBoundary><Literal>A</Literal></LowerBoundary>" + + "<UpperBoundary><Literal>C</Literal></UpperBoundary>" + + "</PropertyIsBetween>"); + + String test = "02.field0=B,02.field1=C,02.field2=D,02.field3=E,02.field4=F," + + "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E," + + "03.field0=C,03.field1=D,03.field2=E,03.field3=F,03.field4=G"; + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testMultiLevelIterator() { + Scanner s = getCellLevelScanner(); + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + + s = setUpIntersectingIterator(s, terms, true); + + try { + s.setScanIterators(60, CellLevelFilteringIterator.class.getName(), "fi"); + } catch (IOException e) { + e.printStackTrace(); + } + + s.setScanIteratorOption("fi", CellLevelFilteringIterator.OPTION_FILTER, "<PropertyIsEqualTo><PropertyName>field0</PropertyName>" + + "<Literal>A</Literal>" + + "</PropertyIsEqualTo>"); + + String test = "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E"; + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testMultiLevelIterator2() { + Scanner s = getCellLevelScanner(); + s = setUpSortedRangeIterator(s, true); + try { + s.setScanIterators(60, CellLevelFilteringIterator.class.getName(), "fi"); + } catch (IOException e) { + e.printStackTrace(); + } + s.setScanIteratorOption("fi", CellLevelFilteringIterator.OPTION_FILTER, "<PropertyIsEqualTo><PropertyName>field0</PropertyName>" + + "<Literal>A</Literal>" + + "</PropertyIsEqualTo>"); + + String test = "01.field0=A,01.field1=B,01.field2=C,01.field3=D,01.field4=E"; + assertTrue(test.equals(checkCellLevel(s))); + } + + @Test + public void testCellLevelRecordIterator() { + Scanner s = getCellLevelScanner(); + s = setUpSortedRangeIterator(s, true); + try { + s.setScanIterators(60, CellLevelRecordIterator.class.getName(), "recordItr"); + } catch (IOException e) { + e.printStackTrace(); + } + +// for (Entry<Key, Value> e: s) { +// String v = e.getValue().toString(); +// v = v.replaceAll("\\u0000", ","); +// v = v.replaceAll("\\uFFFD", "="); +// System.out.println(e.getKey() + "\t" + v); +// } + String test = "02,01,03"; + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testIntersectionWithoutDocLookup() { + Text[] terms = new Text[] { + new Text("C"), + new Text("D") + }; + // all the evens will come first + String test = "\u000002,\u000001,\u000003"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.OPTION_DOC_LOOKUP, "false"); + s.setRange(new Range()); + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testSimpleNot() { + Text[] terms = new Text[] { + new Text("B"), + new Text("F") + }; + + boolean[] nots = new boolean[] { + false, + true + }; + + String test="01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.notFlagOptionName, GMDenIntersectingIterator.encodeBooleans(nots)); + s.setRange(new Range()); + + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testRangeNot() { + Text[] terms = new Text[] { + new Text("B"), + new Text(GMDenIntersectingIterator.getRangeTerm("index", "F", true, "H", true)) + }; + + boolean[] nots = new boolean[] { + false, + true + }; + + String test = "01"; + Scanner s = setUpIntersectingIterator(getSerializedScanner(), terms, false); + s.setScanIteratorOption("ii", GMDenIntersectingIterator.notFlagOptionName, GMDenIntersectingIterator.encodeBooleans(nots)); + s.setRange(new Range()); + + assertTrue(test.equals(checkSerialized(s))); + } + + @Test + public void testMinIteratorOnLastKeys() { + Scanner s = getSerializedScanner(); + try { + s.setScanIterators(50, SortedMinIterator.class.getName(), "min"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + s.setScanIteratorOption("min", SortedMinIterator.OPTION_PREFIX, "z"); + s.setRange(new Range()); + + String test = "02,04,06,08,10,01,03,05,07,09"; + assertTrue(test.equals(checkSerialized(s))); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/test/java/JTSFilterTest.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/test/java/JTSFilterTest.java b/partition/common-query/src/test/java/JTSFilterTest.java new file mode 100644 index 0000000..8224f64 --- /dev/null +++ b/partition/common-query/src/test/java/JTSFilterTest.java @@ -0,0 +1,181 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.hadoop.io.Text; +import org.junit.Test; + +import ss.cloudbase.core.iterators.GMDenIntersectingIterator; +import ss.cloudbase.core.iterators.filter.jts.JTSFilter; + +import cloudbase.core.client.Connector; +import cloudbase.core.client.Scanner; +import cloudbase.core.client.TableNotFoundException; +import cloudbase.core.data.Key; +import cloudbase.core.data.Range; +import cloudbase.core.data.Value; +import cloudbase.core.iterators.FilteringIterator; +import cloudbase.core.security.Authorizations; + +/** + * + * @author rashah + */ +public class JTSFilterTest +{ + + private Connector cellLevelConn; + private Connector serializedConn; + private static final String TABLE = "partition"; + private static final Authorizations AUTHS = new Authorizations("ALPHA,BETA,GAMMA".split(",")); + + + + protected Connector getSerializedConnector() + { + if (serializedConn == null) + { + serializedConn = SampleJTSData.initConnector(); + SampleJTSData.writeDenSerialized(serializedConn, SampleJTSData.sampleData()); + } + return serializedConn; + } + + + + protected Scanner getSerializedScanner() + { + Connector c = getSerializedConnector(); + try + { + return c.createScanner(TABLE, AUTHS); + } + catch (TableNotFoundException e) + { + return null; + } + } + + protected Scanner setUpJTSFilter(Scanner s, String latitude, String longitude, boolean change_name) + { + try + { + + s.setScanIterators(50, FilteringIterator.class.getName(), "gvdf"); + s.setScanIteratorOption("gvdf", "0", JTSFilter.class.getName()); + s.setScanIteratorOption("gvdf", "0." + JTSFilter.OPTIONCenterPointLat, latitude); + s.setScanIteratorOption("gvdf", "0." + JTSFilter.OPTIONCenterPointLon, longitude); + if (change_name) + s.setScanIteratorOption("gvdf", "0." + JTSFilter.OPTIONGeometryKeyName, "beam-footprint"); + + + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return s; + } + + protected String checkSerialized(Scanner s) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Entry<Key, Value> e : s) + { + + if (!first) + { + sb.append(","); + } + else + { + first = false; + } + + String colq = e.getKey().getColumnQualifier().toString(); + + sb.append(colq); + } + return sb.toString(); + } + + + @Test + public void testNoResults() + { + //London is in niether - 51°30'0.00"N 0° 7'0.00"W + String latitude = "51.5"; + String longitude = "0.11"; + + Scanner s = setUpJTSFilter(getSerializedScanner(), latitude, longitude, false); + s.setRange(new Range()); + +// System.out.println("{" + checkSerialized(s) + "}"); + assertTrue(checkSerialized(s).isEmpty()); + } + + + @Test + public void testOneResultAmerica() + { + //This is North America + //Points 39°44'21.00"N 104°59'3.00"W (Denver) are in the footprint + String latitude = "33"; + String longitude = "-93.0"; + + Scanner s = setUpJTSFilter(getSerializedScanner(), latitude, longitude, false); + s.setRange(new Range()); + + System.out.println("{" + checkSerialized(s) + "}"); + assertTrue(checkSerialized(s).equals("02")); + } + + + @Test + public void testOneResultAustralia() + { + //This is Australia + //Points like 22S 135E are in the beam + String latitude = "-9"; + String longitude = "100.0"; + + Scanner s = setUpJTSFilter(getSerializedScanner(), latitude, longitude, false); + s.setRange(new Range()); + + System.out.println("{" + checkSerialized(s) + "}"); + assertTrue(checkSerialized(s).equals("01")); + } + + @Test + public void testOneResultHawaii() + { + // -164 40 - somewhere near hawaii + + //This is Australia + //Points like 22S 135E are in the beam + String latitude = "40"; + String longitude = "-164.0"; + + Scanner s = setUpJTSFilter(getSerializedScanner(), latitude, longitude, true); + s.setRange(new Range()); + + System.out.println("{" + checkSerialized(s) + "}"); + assertTrue(checkSerialized(s).equals("03")); + } + + + @Test + public void testDummyTest() + { + assertTrue(true); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/92ddfa59/partition/common-query/src/test/java/OGCFilterTest.java ---------------------------------------------------------------------- diff --git a/partition/common-query/src/test/java/OGCFilterTest.java b/partition/common-query/src/test/java/OGCFilterTest.java new file mode 100644 index 0000000..fd54945 --- /dev/null +++ b/partition/common-query/src/test/java/OGCFilterTest.java @@ -0,0 +1,163 @@ +import java.util.HashMap; +import java.util.Map; + +import org.apache.hadoop.io.Text; +import org.junit.Test; + +import cloudbase.core.data.Key; +import cloudbase.core.data.Value; +import ss.cloudbase.core.iterators.filter.ogc.OGCFilter; + +import static org.junit.Assert.*; + +public class OGCFilterTest { + private Key testKey = new Key(new Text("row"), new Text("colf"), new Text("colq")); + private Value testValue = new Value("uuid~event\uFFFDmy-event-hash-1\u0000date\uFFFD20100819\u0000time~dss\uFFFD212706.000\u0000frequency\uFFFD3.368248181443644E8\u0000latitude\uFFFD48.74571142707959\u0000longitude\uFFFD13.865561564126812\u0000altitude\uFFFD1047.0\u0000datetime\uFFFD2010-08-19T21:27:06.000Z\u0000test~key\uFFFD\u0000key\uFFFDa\uFFFDb".getBytes()); + + public OGCFilterTest() { + + } + + private OGCFilter getFilter(String filter) { + OGCFilter f = new OGCFilter(); + Map<String, String> options = new HashMap<String, String>(); + options.put(OGCFilter.OPTION_FILTER, filter); + f.init(options); + return f; + } + + @Test + public void testBBOX() { + OGCFilter f = getFilter("<BBOX><gml:Envelope>" + + "<gml:LowerCorner>13 48</gml:LowerCorner>" + + "<gml:UpperCorner>14 49</gml:UpperCorner>" + + "</gml:Envelope></BBOX>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testBetweenStr() { + OGCFilter f = getFilter("<PropertyIsBetween><PropertyName>datetime</PropertyName>" + + "<LowerBoundary><Literal>2010-08-19</Literal></LowerBoundary>" + + "<UpperBoundary><Literal>2010-08-20</Literal></UpperBoundary>" + + "</PropertyIsBetween>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testBetweenNum() { + OGCFilter f = getFilter("<PropertyIsBetween><PropertyName>frequency</PropertyName>" + + "<LowerBoundary><Literal>330000000</Literal></LowerBoundary>" + + "<UpperBoundary><Literal>340000000</Literal></UpperBoundary>" + + "</PropertyIsBetween>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testEqualStr() { + OGCFilter f = getFilter("<PropertyIsEqualTo><PropertyName>uuid~event</PropertyName><Literal>my-event-hash-1</Literal></PropertyIsEqualTo>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testEqualNum() { + OGCFilter f = getFilter("<PropertyIsEqualTo><PropertyName>altitude</PropertyName><Literal>1047</Literal></PropertyIsEqualTo>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testGreaterThanStr() { + OGCFilter f = getFilter("<PropertyIsGreaterThan><PropertyName>datetime</PropertyName><Literal>2010-08-15</Literal></PropertyIsGreaterThan>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testGreaterThanNum() { + OGCFilter f = getFilter("<PropertyIsGreaterThan><PropertyName>altitude</PropertyName><Literal>1000</Literal></PropertyIsGreaterThan>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testLessThanStr() { + OGCFilter f = getFilter("<PropertyIsLessThan><PropertyName>datetime</PropertyName><Literal>2010-08-20</Literal></PropertyIsLessThan>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testLessThanNum() { + OGCFilter f = getFilter("<PropertyIsLessThan><PropertyName>altitude</PropertyName><Literal>1200</Literal></PropertyIsLessThan>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testLike() { + OGCFilter f = getFilter("<PropertyIsLike><PropertyName>uuid~event</PropertyName><Literal>*event*</Literal></PropertyIsLike>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testNotEqualNum() { + OGCFilter f = getFilter("<PropertyIsNotEqualTo><PropertyName>altitude</PropertyName><Literal>1046</Literal></PropertyIsNotEqualTo>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testNull() { + OGCFilter f = getFilter("<PropertyIsNull><PropertyName>test~key</PropertyName></PropertyIsNull>"); + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testNot() { + OGCFilter f = getFilter("<Not><PropertyIsEqualTo><PropertyName>altitude</PropertyName><Literal>1047</Literal></PropertyIsEqualTo></Not>"); + assertFalse(f.accept(testKey, testValue)); + } + + @Test + public void testAnd() { + OGCFilter f = getFilter("<And>" + + "<PropertyIsEqualTo><PropertyName>altitude</PropertyName><Literal>1047</Literal></PropertyIsEqualTo>" + + "<PropertyIsNull><PropertyName>test~key</PropertyName></PropertyIsNull>" + + "</And>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testOr() { + OGCFilter f = getFilter("<Or>" + + "<PropertyIsLike><PropertyName>uuid~event</PropertyName><Literal>*event*</Literal></PropertyIsLike>" + + "<PropertyIsNull><PropertyName>uuid~event</PropertyName></PropertyIsNull>" + + "</Or>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testNand() { + OGCFilter f = getFilter("<Not><And>" + + "<PropertyIsNull><PropertyName>uuid~event</PropertyName></PropertyIsNull>" + + "<PropertyIsNull><PropertyName>test~key</PropertyName></PropertyIsNull>" + + "</And></Not>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testNor() { + OGCFilter f = getFilter("<Not>" + + "<PropertyIsNull><PropertyName>uuid~event</PropertyName></PropertyIsNull>" + + "<PropertyIsNull><PropertyName>altitude</PropertyName></PropertyIsNull>" + + "</Not>"); + + assertTrue(f.accept(testKey, testValue)); + } + + @Test + public void testParse() { + OGCFilter f = getFilter("<PropertyIsEqualTo><PropertyName>key</PropertyName><Literal>a</Literal></PropertyIsEqualTo>"); + assertTrue(f.accept(testKey, testValue)); + } +}
