-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added push down predicates to the core API and supporting classes for…
… those graph database that don't support this notion. the Vertex API has changed.
- Loading branch information
Showing
20 changed files
with
819 additions
and
289 deletions.
There are no files selected for viewing
179 changes: 166 additions & 13 deletions
179
blueprints-core/src/main/java/com/tinkerpop/blueprints/pgm/Filter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
blueprints-core/src/main/java/com/tinkerpop/blueprints/pgm/impls/FilteredEdgeIterable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
|
||
} | ||
|
||
|
||
} | ||
|
||
|
Oops, something went wrong.