Skip to content

Commit

Permalink
added push down predicates to the core API and supporting classes for…
Browse files Browse the repository at this point in the history
… those graph database that don't support this notion. the Vertex API has changed.
  • Loading branch information
okram committed Apr 25, 2012
1 parent 6f0c2eb commit 5841312
Show file tree
Hide file tree
Showing 20 changed files with 819 additions and 289 deletions.
179 changes: 166 additions & 13 deletions blueprints-core/src/main/java/com/tinkerpop/blueprints/pgm/Filter.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,183 @@
package com.tinkerpop.blueprints.pgm;

import java.util.LinkedList;
import java.util.List;

/**
* A Filter is used to constrain what adjacent edges of a vertex are retrieved from the underlying graph storage engine.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class Filter {

public enum Compare {EQUAL, NOT_EQUAL, GREATER_THAN, GREATER_THAN_EQUAL, LESS_THAN, LESS_THAN_EQUAL}

public final String key;
public final Object value;
public final Compare compare;
public final Class<? extends Element> forClass;
public List<Filter> filters = new LinkedList<Filter>();

/**
* Add a property filter to the filter chain.
*
* @param key the key of the property the check
* @param value the value that should be equal to the retrieved property value
* @return the filter chain with new property filter concatenated onto it
*/
public Filter property(final String key, final Object value) {
filters.add(new PropertyFilter(key, value));
return this;
}

/**
* Add a property filter to the filter chain.
*
* @param key the key of the property the check
* @param value the value that should be compared to the retrieved property value
* @param compare the comparator used to determine filtering
* @return the filter chain with new property filter concatenated onto it
*/
public Filter property(final String key, final Object value, final Compare compare) {
filters.add(new PropertyFilter(key, value, compare));
return this;
}

public Filter(final String key, final Object value, final Compare compare, final Class<? extends Element> forClass) {
this.key = key;
this.value = value;
this.compare = compare;
this.forClass = forClass;
/**
* Add a range filter to the filter chain (which is decomposed into two property filters)
* The range is startValue >= value < endValue.
*
* @param key the key of the property to check
* @param startValue the low end of the range
* @param endValue the high end of the range
* @return the filter chain with new range filter concatenated onto it
*/
public Filter range(final String key, final Object startValue, final Object endValue) {
filters.add(new PropertyFilter(key, startValue, Compare.GREATER_THAN_EQUAL));
filters.add(new PropertyFilter(key, endValue, Compare.LESS_THAN));
return this;
}

/**
* A label filter is used to determine if a provided edge has a label contained in the provided labels.
*
* @param labels the labels to check against
* @return the filter chain with new label filter concatenated onto it
*/
public Filter label(final String... labels) {
filters.add(new LabelFilter(labels));
return this;
}

public Filter(final String key, final Object value, final Compare compare) {
this(key, value, compare, Edge.class);
/**
* A label filter is used to determine if a provided edge has a label contained (or not contained) in the provided labels.
*
* @param compare whether the check is inclusive or exclusive of the provided labels
* @param labels the labels to check against
* @return the filter chain with new label filter concatenated onto it
*/
public Filter label(final Compare compare, final String... labels) {
filters.add(new LabelFilter(compare, labels));
return this;
}

public Filter(final String key, final Object value) {
this(key, value, Compare.EQUAL);
/**
* Useful for when the underlying graph engine does not support filtering natively.
* This method can be used to filter the element according to the constructed filters.
*
* @param element the element to be checked.
* @return whether the element should exist or not.
*/
public boolean isLegal(final Element element) {
for (final Filter filter : this.filters) {
if (!filter.isLegal(element))
return false;
}
return true;
}


public class LabelFilter extends Filter {

public String[] labels;
public Compare compare;

public LabelFilter(final Compare compare, final String... labels) {
if (compare != Compare.EQUAL && compare != Compare.NOT_EQUAL) {
throw new IllegalArgumentException("Only comparators of EQUAL and NOT_EQUAL are allowed: " + compare);
}
this.compare = compare;
this.labels = labels;
}

public LabelFilter(final String... labels) {
this(Compare.EQUAL, labels);
}

public boolean isLegal(final Element edge) {
final String edgeLabel = ((Edge) edge).getLabel();
if (compare == Compare.EQUAL) {
for (final String label : this.labels) {
if (label.equals(edgeLabel))
return true;
}
return false;
} else if (compare == Compare.NOT_EQUAL) {
for (final String label : this.labels) {
if (label.equals(edgeLabel))
return false;
}
return true;
} else {
throw new IllegalArgumentException("Invalid state as no valid filter was provided");
}
}
}

public class PropertyFilter extends Filter {

public final String key;
public final Object value;
public final Compare compare;

public PropertyFilter(final String key, final Object value, final Compare compare) {
this.key = key;
this.value = value;
this.compare = compare;
}

public PropertyFilter(final String key, final Object value) {
this(key, value, Compare.EQUAL);
}

public boolean isLegal(final Element element) {
final Object elementValue = element.getProperty(key);

switch (compare) {
case EQUAL:
if (null == elementValue)
return value == null;
return elementValue.equals(value);
case NOT_EQUAL:
if (null == elementValue)
return value != null;
return !elementValue.equals(value);
case GREATER_THAN:
if (null == elementValue || value == null)
return false;
return ((Comparable) elementValue).compareTo(value) >= 1;
case LESS_THAN:
if (null == elementValue || value == null)
return false;
return ((Comparable) elementValue).compareTo(value) <= -1;
case GREATER_THAN_EQUAL:
if (null == elementValue || value == null)
return false;
return ((Comparable) elementValue).compareTo(value) >= 0;
case LESS_THAN_EQUAL:
if (null == elementValue || value == null)
return false;
return ((Comparable) elementValue).compareTo(value) <= 0;
default:
throw new IllegalArgumentException("Invalid state as no valid filter was provided");
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,29 @@
*/
public interface Vertex extends Element {

public static final String TYPE_ERROR_MESSAGE = "The provided Object[] can only be of type String of Filter";

/**
* The edges emanating from, or leaving, the vertex.
* The provided filter objects can either be String or Filter.
* If String, then the edge label must equal the provided String.
* If Filter, then the edge must meet the Filter criteria.
*
* @param labels the labels of the edges to return
* @param filters the filters of the edges to return
* @return the edges for which the vertex is the tail
*/
public Iterable<Edge> getOutEdges(String... labels);
public Iterable<Edge> getOutEdges(Object... filters);

/**
* The edges incoming to, or arriving at, the vertex.
* The provided filter objects can either be String or Filter.
* If String, then the edge label must equal the provided String.
* If Filter, then the edge must meet the Filter criteria.
*
* @param labels the labels of the edges to return
* @param filters the labels of the edges to return
* @return the edges for which the vertex is the head
*/
public Iterable<Edge> getInEdges(String... labels);
public Iterable<Edge> getInEdges(Object... filters);

//public T getRawVertex();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.tinkerpop.blueprints.pgm.impls;

import com.tinkerpop.blueprints.pgm.Edge;
import com.tinkerpop.blueprints.pgm.Filter;
import com.tinkerpop.blueprints.pgm.Vertex;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class FilteredEdgeIterable implements Iterable<Edge> {

private final Iterable<Edge> edgeIterable;
private final List<String> labels;
private Filter filter = null;

public FilteredEdgeIterable(final Iterable<Edge> edgeIterable, final Object... filters) {
this.edgeIterable = edgeIterable;
this.labels = new LinkedList<String>();
for (final Object filter : filters) {
if (filter instanceof String) {
labels.add((String) filter);
} else if (filter instanceof Filter) {
this.filter = (Filter) filter;
} else {
throw new IllegalArgumentException(Vertex.TYPE_ERROR_MESSAGE);

}
}
}

public FilteredEdgeIterable(final Iterable<Edge> edgeIterable, final List<String> labels, final Filter filter) {
this.edgeIterable = edgeIterable;
this.labels = labels;
this.filter = filter;
}

public FilteredEdgeIterable(final Iterable<Edge> edgeIterable, final Filter filter) {
this.edgeIterable = edgeIterable;
this.labels = Collections.emptyList();
this.filter = filter;
}

public FilteredEdgeIterable(final Iterable<Edge> edgeIterable, final List<String> labels) {
this.edgeIterable = edgeIterable;
this.labels = labels;
this.filter = null;
}

public Iterator<Edge> iterator() {
return new FilteredEdgeIterator(this.edgeIterable.iterator());
}

public static Filter getFilter(final Object... filters) {
for (final Object filter : filters) {
if (filter instanceof Filter) {
return (Filter) filter;
} else if (!(filter instanceof String))
throw new IllegalArgumentException(Vertex.TYPE_ERROR_MESSAGE);
}
return null;
}

public static List<String> getLabels(final Object... filters) {
List<String> list = new LinkedList<String>();
for (final Object filter : filters) {
if (filter instanceof String) {
list.add((String) filter);
} else if (!(filter instanceof Filter))
throw new IllegalArgumentException(Vertex.TYPE_ERROR_MESSAGE);
}
return list;
}

private final class FilteredEdgeIterator implements Iterator<Edge> {

private final Iterator<Edge> edgeIterator;
private Edge nextEdge;


public FilteredEdgeIterator(final Iterator<Edge> edgeIterator) {
this.edgeIterator = edgeIterator;
}

public void remove() {
throw new UnsupportedOperationException();
}

public boolean hasNext() {
return this.nextEdge != null || this.loadNext();
}

public Edge next() {
if (this.nextEdge != null) {
final Edge temp = this.nextEdge;
this.nextEdge = null;
return temp;
} else {
if (this.loadNext()) {
final Edge temp = this.nextEdge;
this.nextEdge = null;
return temp;
} else {
throw new NoSuchElementException();
}
}
}

private boolean loadNext() {
while (this.edgeIterator.hasNext()) {
final Edge edge = this.edgeIterator.next();

boolean keep = false;
if (!labels.isEmpty()) {
final String edgeLabel = edge.getLabel();
for (final String label : labels) {
if (edgeLabel.equals(label)) {
keep = true;
break;
}
}
} else {
keep = true;
}

if (!keep)
continue;


if (null != filter)
keep = filter.isLegal(edge);
else
keep = true;

if (keep) {
this.nextEdge = edge;
return true;
}
}

return false;
}


}


}


Loading

0 comments on commit 5841312

Please sign in to comment.